package geometry;

import hulppakket.Intersection;
import hulppakket.Ray;
import hulppakket.Vector3f;

import java.util.ArrayList;

import materials.Material;
import textures.Texture;

public class BVHNode implements Primitive {

	private BoundingBox boundingBox;
	private Primitive left, right;

	// TODO eventueel nog enum maken voor axis
	public BVHNode(ArrayList<Primitive> primitives, int axis) {
		int n = primitives.size();
		if (n == 1) {
			left = primitives.get(0);
			right = null;
			boundingBox = left.getBoundingBox();
		} else if (n == 2) {
			left = primitives.get(0);
			right = primitives.get(1);
			boundingBox = left.getBoundingBox().combine(right.getBoundingBox());
		} else {
			ArrayList<Primitive> leftPrimitives = new ArrayList<Primitive>();
			ArrayList<Primitive> rightPrimitives = new ArrayList<Primitive>();
			try {
				split(primitives, axis, leftPrimitives, rightPrimitives);
				left = new BVHNode(leftPrimitives, (axis + 1) % 3);
				right = new BVHNode(rightPrimitives, (axis + 1) % 3);
				boundingBox = left.getBoundingBox().combine(
						right.getBoundingBox());
			} catch (Exception e) {
				System.out.println(e.getMessage());
			}
		}
	}

	@Override
	public BoundingBox getBoundingBox() {
		return this.boundingBox;
	}

	@Override
	public Material getMaterial() {
		// TODO Auto-generated method stub
		return null;
	}

	// TODO Eventueel versnellen door lijst telkens te verdelen in 2.
	private float getMidPoint(ArrayList<Primitive> primitives, int axis)
			throws Exception {
		BoundingBox bbox = primitives.get(0).getBoundingBox();
		for (Primitive prim : primitives) {
			bbox = bbox.combine(prim.getBoundingBox());
		}
		return (bbox.getMin().getCoordinate(axis) / 2f + bbox.getMax()
				.getCoordinate(axis) / 2f);
	}

	// TODO verplaatsen naar Primitive?
	private float getMidPoint(Primitive primitive, int axis) throws Exception {
		return (primitive.getBoundingBox().getMin().getCoordinate(axis) / 2f + primitive
				.getBoundingBox().getMax().getCoordinate(axis) / 2f);
	}

	@Override
	public Vector3f getNormal(float alfa, float beta, float gamma) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Intersection intersect(Ray ray, float t0, float t1) {
		Intersection leftHit = null;
		Intersection rightHit = null;
		if (this.boundingBox.hit(ray, t0, t1)) {
			if (left != null)
				leftHit = this.left.intersect(ray, t0, t1);
			if (right != null)
				rightHit = this.right.intersect(ray, t0, t1);
			if (leftHit != null && rightHit != null) {
				if (leftHit.getT() < rightHit.getT())
					return leftHit;
				else
					return rightHit;
			} else if (leftHit != null)
				return leftHit;
			else if (rightHit != null)
				return rightHit;
			else
				return null;
		} else
			return null;
	}

	private void split(ArrayList<Primitive> primitives, int axis,
			ArrayList<Primitive> smaller, ArrayList<Primitive> larger)
			throws Exception {
		float midPoint = getMidPoint(primitives, axis);
		float average;
		for (Primitive prim : primitives) {
			average = this.getMidPoint(prim, axis);
			if (average < midPoint) {
				smaller.add(prim);
			} else {
				larger.add(prim);
			}
		}
		if (primitives.size() == smaller.size()) {
			larger.add(smaller.remove(smaller.size()-1));
		} else if (primitives.size() == larger.size()) {
			smaller.add(larger.remove(larger.size()-1));
		}
	}

	@Override
	public Texture getTexture() {
		// TODO Auto-generated method stub
		return null;
	}
}
